﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;

namespace WebPALTT.Lib
{
    /// <summary>
    /// This class contains the results of an action. 
    /// </summary>
    [Serializable]
    public class ActionResults : ISerializable
    {
        protected String mName;
        protected Guid mKey;
        protected ActionType mActionType;
        protected String mCustomType;
        List<string> mErrors;
        List<string> mWarnings;
        protected Dictionary<string, object> mData;
        protected Dictionary<string, object> mResults;
        /// <summary>
        /// The name of the action used to create the result.
        /// </summary>
        public String Name { get { return mName; } set { mName = value; } }
        /// <summary>
        /// The key of the action used to create the result.
        /// </summary>
        public Guid Key { get { return mKey; } set { mKey = value; } }
        /// <summary>
        /// The ActionType of the action used to create the result.
        /// </summary>
        public ActionType ActionType { get { return mActionType; } set { mActionType = value; } }
        /// <summary>
        /// The type of the action used to create the result.
        /// </summary>
        public String CustomType { get { return mCustomType; } set { mCustomType = value; } }
        /// <summary>
        /// Returns true if the result has errors.
        /// </summary>
        public bool HasErrors { get { return mErrors.Count > 0 ? true : false; } }
        /// <summary>
        /// Gets a list of errors in the result.
        /// </summary>
        public List<string> Errors { get { return mErrors; } }
        /// <summary>
        /// Returns true if the result has warnings.
        /// </summary>
        public bool HasWarnings { get { return mWarnings.Count > 0 ? true : false; } }
        /// <summary>
        /// Returns a list of warnings in the result.
        /// </summary>
        public List<string> Warnings { get { return mWarnings; } }
        /// <summary>
        /// Gets the list of data attached to the action. The data depends on the type of action.
        /// </summary>
        public Dictionary<string, object> Data { get { return mData; } }
        /// <summary>
        /// Gets the list of results attached to the action.
        /// </summary>
        public Dictionary<string, object> Results { get { return mResults; } }

        public ActionResults() : this("", Guid.Empty, ActionType.Action, "") { }

        public ActionResults(string name, Guid key, Lib.ActionType actionType, string customType)
        {
            this.mName = name;
            this.mKey = key;
            this.mActionType = actionType;
            this.mCustomType = customType;
            this.mErrors = new List<string> { };
            this.mWarnings = new List<string> { };
            this.mData = new Dictionary<string, object> { };
            this.mResults = new Dictionary<string, object> { };
        }

        public ActionResults(SerializationInfo info, StreamingContext ctxt)
        {
            mErrors = (List<string>)info.GetValue("Errors", typeof(List<string>));
            mWarnings = (List<string>)info.GetValue("Warnings", typeof(List<string>));
            mData = (Dictionary<string, object>)info.GetValue("Data", typeof(Dictionary<string, object>));
            mResults = (Dictionary<string, object>)info.GetValue("Results", typeof(Dictionary<string, object>));
            mName = info.GetString("Name");
            mKey = (Guid)info.GetValue("Key", typeof(Guid));
            mActionType = (ActionType)info.GetValue("ActionType", typeof(ActionType));
            mCustomType = info.GetString("CustomType");
        }

        public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
        {
            info.AddValue("Errors", mErrors);
            info.AddValue("Warnings", mWarnings);
            info.AddValue("Data", mData);
            info.AddValue("Results", mResults);
            info.AddValue("Name", mName);
            info.AddValue("Key", mKey);
            info.AddValue("ActionType", mActionType);
            info.AddValue("CustomType", mCustomType);
        }
    }
    
    /// <summary>
    /// This class contains the results of a scenario. 
    /// </summary>
    [Serializable]
    public class ScenarioResults : ISerializable
    {
        protected Guid mKey;
        public int VirtualUserNo = 0;
        public TimeSpan Duration;
        protected ActionResultsCollection mResults = new ActionResultsCollection();
        /// <summary>
        /// The Key of the scenario used to create the results
        /// </summary>
        public Guid Key { get { return mKey; } set { mKey = value; } }
        /// <summary>
        /// Collection of ActionResult objects.
        /// </summary>
        public ActionResultsCollection Results { get { return mResults; } }

        public ScenarioResults(Guid key) 
        {
            mKey = key;
        }

        public ScenarioResults(SerializationInfo info, StreamingContext ctxt)
        {
            try { mResults = (ActionResultsCollection)info.GetValue("Results", typeof(ActionResultsCollection)); }
            catch (SerializationException)
            {
                mResults = new ActionResultsCollection();
                mResults.AddRange((List<ActionResults>)info.GetValue("Results", typeof(List<ActionResults>)));
            }
            mKey = (Guid)info.GetValue("Key", typeof(Guid));
            VirtualUserNo = info.GetInt32("VirtualUserNo");
            Duration = (TimeSpan)info.GetValue("Duration", typeof(TimeSpan));
        }

        public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
        {   
            info.AddValue("Results", mResults);
            info.AddValue("Key", mKey);
            info.AddValue("VirtualUserNo", VirtualUserNo);
            info.AddValue("Duration", Duration);
        }

        /// <summary>
        /// Returns a boolean indicating if the actions in the scenario have any warings or errors.
        /// </summary>
        /// <param name="warnings">if true will search for warnings (default is true)</param>
        /// <param name="errors">if true will search for errors (default is true)</param>
        /// <returns></returns>
        public bool HasMessages(bool warnings = true, bool errors = true)
        {
            foreach (ActionResults action in mResults)
            {
                if (action.Results != null)
                {
                    if (action.HasErrors && errors)
                        return true;
                    if (action.HasWarnings && warnings)
                        return true;
                }
            }
            return false;
        }

        /// <summary>
        /// Returns an array of warinings and errors from the scenario.
        /// </summary>
        /// <param name="warnings">if true will add warnings (default is true)</param>
        /// <param name="errors">if true will add errors (default is true)</param>
        /// <returns></returns>
        public string[] GetMessages(bool warnings = true, bool errors = true)
        {
            List<string> messages = new List<string> { };
            foreach (ActionResults action in mResults)
            {
                if (action.Results != null)
                {
                    if (errors)
                        messages.AddRange(action.Errors);
                    if (warnings)
                        messages.AddRange(action.Warnings);
                }
            }
            return messages.ToArray();
        }

        /// <summary>
        /// Returns an integer counting the number of warinings and errors from the scenario.
        /// </summary>
        /// <param name="warnings">if true will count warnings (default is true)</param>
        /// <param name="errors">if true will count errors (default is true)</param>
        /// <returns></returns>
        public int GetMessageCount(bool warnings = true, bool errors = true)
        {
            int count = 0;
            foreach (ActionResults action in mResults)
            {
                if (action.Results != null)
                {
                    if (errors)
                        count += action.Errors.Count;
                    if (warnings)
                        count += action.Warnings.Count;
                }
            }
            return count;
        }
    }

    /// <summary>
    /// This class contains a collection of ActionResults objects.
    /// The class inherits from IList, ICollection and IEnumerable.
    /// </summary>
    [Serializable()]
    public class ActionResultsCollection
        : IList<ActionResults>, ICollection<ActionResults>, IEnumerable<ActionResults>, ISerializable
    {
        public IEnumerator<ActionResults> GetEnumerator() { return mActions.GetEnumerator(); }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return mActions.GetEnumerator(); }

        public bool IsReadOnly { get { return false; } }

        public ActionResultsCollection(SerializationInfo info, StreamingContext ctxt)
        {
            mActions = (List<ActionResults>)info.GetValue("Actions", typeof(List<ActionResults>));
        }

        public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
        {
            info.AddValue("Actions", mActions);
        }

        List<ActionResults> mActions = new List<ActionResults> { };

        public ActionResultsCollection() { }

        public int Count { get { return mActions.Count; } }

        public ActionResults this[int index]
        {
            get { return mActions[index]; }
            set { mActions[index] = value; }
        }

        public ActionResults this[string name]
        {
            get
            {
                int index = IndexOf(name);
                if (index >= 0)
                    return mActions[index];
                return null;
            }
            set
            {
                int index = IndexOf(name);
                if (index >= 0)
                    mActions[index] = value;
                else
                    throw new IndexOutOfRangeException("Index with name [" + name + "] was not found and could not be assigned to.");
            }
        }

        public ActionResults this[Guid key]
        {
            get
            {
                int index = IndexOf(key);
                if (index >= 0)
                    return mActions[index];
                return null;
            }
            set
            {
                int index = IndexOf(key);
                if (index >= 0)
                    mActions[index] = value;
                else
                    throw new IndexOutOfRangeException("Index with key {" + key.ToString() + "} was not found and could not be assigned to.");
            }
        }

        public void Add(ActionResults item)
        {
            mActions.Add(item);
        }

        public void AddRange(IEnumerable<ActionResults> collection)
        {
            mActions.AddRange(collection);
        }

        public void Clear()
        {
            mActions.Clear();
        }

        public bool Contains(ActionResults item) { return mActions.Contains(item); }

        public bool Contains(Guid key)
        {
            foreach (ActionResults item in mActions)
            {
                if (Guid.Equals(item.Key, key))
                    return true;
            }
            return false;
        }

        public bool Contains(ActionResults value, IEqualityComparer<ActionResults> comparer) { return mActions.Contains(value, comparer); }

        public void CopyTo(ActionResults[] array, int arrayIndex) { mActions.CopyTo(array, arrayIndex); }

        public ActionResults[] FindAll(Predicate<ActionResults> match)
        {
            return mActions.FindAll(match).ToArray();
        }

        public ActionResults Find(Predicate<ActionResults> match)
        {
            return mActions.Find(match);
        }

        public int IndexOf(ActionResults value) { return mActions.IndexOf(value); }

        public int IndexOf(string name)
        {
            for (int i = 0; i < mActions.Count; i++)
                if (mActions[i].Name.Equals(name)) { return i; }
            return -1;
        }

        public int IndexOf(Guid key)
        {
            for (int i = 0; i < mActions.Count; i++)
                if (Guid.Equals(mActions[i].Key, key)) { return i; }
            return -1;
        }

        public void Insert(int index, ActionResults value)
        {
            mActions.Insert(index, value);
        }

        public bool Remove(ActionResults value)
        {
            return mActions.Remove(value);
        }

        public bool Remove(string name)
        {
            int index = IndexOf(name);
            if (index >= 0)
            {
                mActions.RemoveAt(index);
                return true;
            }
            return false;
        }

        public bool Remove(Guid key)
        {
            int index = IndexOf(key);
            if (index >= 0)
            {
                mActions.RemoveAt(index);
                return true;
            }
            return false;
        }

        public void RemoveAt(int index)
        {
            mActions.RemoveAt(index);
        }
    }
}
